//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#ifndef __ELASTOS_INTERLOCK_H__
#define __ELASTOS_INTERLOCK_H__

#ifndef _XINHAO
#include <_atomic.h>
#else
#include <regs.h>
#include <elatypes.h>
#endif

INLINE void __Sti()
{
#if defined(_GNUC)
    ASM (
        "mfc0   $15, $12;" // $15: t7;     $12: cp0_status
        "ori    $15, $15, 0x1;" // set SR(IE)
        "mtc0   $15, $12;");
#elif defined(_EVC)
    ASM (
        ".set   noreorder;"
        "mfc0   t7, $12;"
        "ori    t7, t7, 0x1;"
        "mtc0   t7, $12;"
        ".set   reorder;");
#else
#error unknown compiler
#endif
}

INLINE uint32_t __SaveFlagsAndCli()
{
    uint32_t uFlags;

#if defined(_GNUC)
    ASM(
        "mfc0   %0, $12;"
        "or     $15, %0, $0;"
        "ori    $15, $15, 0x1;" // clear SR(IE)
        "xori   $15, $15, 0x1;"
        "mtc0   $15, $12;"
        :"=r&"(uFlags)::"$15");
#elif defined(_EVC)
    ASM(
        ".set   noreorder;"
        "mfc0   t7, $12;"
        "sw     t7, 0(%0);"
        "ori    t7, t7, 0x1;"
        "xori   t7, t7, 0x1;"
        "mtc0   t7, $12;"
        ".set   reorder;", &uFlags);
#else
#error unknown compiler
#endif

    return uFlags & 0x1;
}

INLINE void __RestoreIF(uint_t uFlags)
{
    if (uFlags) __Sti();
}

#define InterlockedIncrement(x)             __InterlockedIncrement(x)
#define InterlockedDecrement(x)             __InterlockedDecrement(x)
#define InterlockedExchangeAdd(x, y)        __InterlockedExchangeAdd(x, y)
#define InterlockedExchange(x, y)           __InterlockedExchange(x, y)
#define InterlockedExchangePointer(x, y)    __InterlockedExchangePointer(x, y)
#define InterlockedCompareExchange(x,y,z)   __InterlockedCompareExchange(x,y,z)
#define InterlockedCompareExchangePointer(x, y, z) \
            __InterlockedCompareExchangePointer(x, y, z)

#ifdef _XINHAO

#ifndef __ATOMIC_H_
#define __ATOMIC_H_

#define     FORMAL     1

INLINE Int32 __InterlockedIncrement(PInt32 plAddend)
{
    Int32 lOriginalAddend;

#if FORMAL
    uint32_t    uFlags = __SaveFlagsAndCli();
    lOriginalAddend = ++(*plAddend);
    __RestoreIF(uFlags);
#else
    ASM(
    "InterlockedIncrement_1:"
        "ll     $14, 0(%1);"
        "addiu  $14, $14, 1;"
        "sw     $14, 0(%0);"
        "sc     $14, 0(%1);"
        "beqz   $14, InterlockedIncrement_1;",
        &lOriginalAddend, plAddend);
#endif

    return lOriginalAddend;
}

INLINE Int32 __InterlockedDecrement(PInt32 plAddend)
{
    Int32 lOriginalAddend;

#if FORMAL
    uint32_t    uFlags = __SaveFlagsAndCli();
    lOriginalAddend = --(*plAddend);
    __RestoreIF(uFlags);
#else
    ASM(
    "InterlockedDecrement_1:"
        "ll     $14, (%1);"
        "addiu  $14, $14, -1;"
        "sw     $14, 0(%0);"
        "sc     $14, (%1);"
        "beqz   $14, InterlockedDecrement_1;",
        &lOriginalAddend, plAddend);
#endif

    return lOriginalAddend;
}

INLINE Int32 __InterlockedExchangeAdd(PInt32 plAddend, Int32 lIncrement)
{
    Int32 lOriginalAddend;

#if FORMAL
    uint32_t    uFlags = __SaveFlagsAndCli();
    lOriginalAddend = *plAddend;
    *plAddend += lIncrement;
    __RestoreIF(uFlags);
#else
    ASM(
        "move   $14, %2;"
    "InterlockedExchangeAdd_1:"
        "ll     $15, (%1);"
        "add    $13, $15, $14;"
        "sc     $13, (%1);"
        "beqz   $13, InterlockedExchangeAdd_1;"
        "sw     $15, 0(%0);",
        &lOriginalAddend, plAddend, lIncrement);
#endif

    return lOriginalAddend;
}

INLINE Int32 __InterlockedExchange(PInt32 plTarget, Int32 lValue)
{
    Int32 lOriginalTarget;

#if FORMAL
    uint32_t    uFlags = __SaveFlagsAndCli();
    lOriginalTarget = *plTarget;
    *plTarget = lValue;
    __RestoreIF(uFlags);
#else
    ASM(
        "move   $14, %2;"
    "InterlockedExchange_1:"
        "ll     $13, 0(%1);"
        "or     $15, $14, $0;"
        "sc     $15, (%1);"
        "beqz   $15, InterlockedExchange_1;"
        "sw     $13, 0(%0);",
        &lOriginalTarget, plTarget, lValue);
#endif

    return lOriginalTarget;
}

INLINE PVoid __InterlockedExchangePointer(PVoid * ppvTarget, PVoid pvValue)
{
    PVoid pvOriginalTarget;

#if FORMAL
    uint32_t    uFlags = __SaveFlagsAndCli();
    pvOriginalTarget = *ppvTarget;
    *ppvTarget = pvValue;
    __RestoreIF(uFlags);
#else
    ASM(
        "move   $14, %2;"
    "InterlockedExchangePointer_1:"
        "ll     $13, (%1);"
        "sw     $13, 0(%0);"
        "or     $15, $14, $0;"
        "sc     $15, (%1);"
        "beqz   $15, InterlockedExchangePointer_1;",
        &pvOriginalTarget, ppvTarget, pvValue);
#endif

    return pvOriginalTarget;
}

INLINE Int32 __InterlockedCompareExchange(PInt32 plDestination,
                Int32 lExchange, Int32 lComparand)
{
    Int32 lOriginalDestination;

#if FORMAL
    uint32_t    uFlags = __SaveFlagsAndCli();
    lOriginalDestination = *plDestination;
    if (lOriginalDestination == lComparand) {
        *plDestination = lExchange;
    }
    __RestoreIF(uFlags);
#else
    ASM(
        "move   t7, %3;"       // load the lComparand
    "InterlockedCompareExchange_2:"
        "ll     t6, (%1);"     // load the plDestination
        "sw     t6, 0(%0);"
        "bne    t7, t6, InterlockedCompareExchange_1;"
        "move   t6, %2;"
    "InterlockedCompareExchange_1:"
        "sc     t6, (%1);"
        "beqz   t6, InterlockedCompareExchange_2;",
        &lOriginalDestination, plDestination,
        lExchange, lComparand);
#endif

    return lOriginalDestination;
}

INLINE PVoid __InterlockedCompareExchangePointer(PVoid * ppvDestination,
                PVoid pvExchange, PVoid pvComparand)
{
    PVoid pvOriginalDestination;

#if FORMAL
    uint32_t    uFlags = __SaveFlagsAndCli();
    pvOriginalDestination = *ppvDestination;
    if (pvOriginalDestination == pvComparand) {
        *ppvDestination = pvExchange;
    }
    __RestoreIF(uFlags);
#else
    ASM(
        "move    $15, %3;"       // load the lComparand
        "InterlockedCompareExchangePointer_2:"
        "ll      $14, (%1);"     // load the plDestination
        "sw      $14, 0(%0);"
        "bne     $15, $14, InterlockedCompareExchangePointer_1;"
        "move    $14, %2;"
        "InterlockedCompareExchangePointer_1:"
        "sc      $14, (%1);"
        "beqz    $14, InterlockedCompareExchangePointer_2;",
        &pvOriginalDestination, ppvDestination,
        pvExchange, pvComparand);
#endif

    return pvOriginalDestination;
}

#endif  // __ATOMIC_H_
#endif  // _XINHAO

#endif //__ELASTOS_INTERLOCK_H__
